package com.rem.chat.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.rem.chat.common.config.AppConfig;
import com.rem.chat.common.constants.Constants;
import com.rem.chat.common.errcode.ServiceErrorCodeConstants;
import com.rem.chat.common.exception.ServiceException;
import com.rem.chat.common.utils.RedisComponent;
import com.rem.chat.common.utils.StringUtil;
import com.rem.chat.mapper.ChatMessageMapper;
import com.rem.chat.mapper.ChatSessionMapper;
import com.rem.chat.mapper.UserContactMapper;
import com.rem.chat.pojo.dataobject.ChatMessageDO;
import com.rem.chat.pojo.dataobject.ChatSessionDO;
import com.rem.chat.pojo.dataobject.UserContactDO;
import com.rem.chat.pojo.request.SendMessageRequest;
import com.rem.chat.pojo.response.SendMessageResponse;
import com.rem.chat.pojo.response.SystemSettingResponse;
import com.rem.chat.service.ChatService;
import com.rem.chat.service.dto.MessageSendDTO;
import com.rem.chat.service.dto.UserLoginDTO;
import com.rem.chat.service.enums.MessageStatusEnum;
import com.rem.chat.service.enums.MessageTypeEnum;
import com.rem.chat.service.enums.UserContactStatusEnum;
import com.rem.chat.service.enums.UserContactTypeEnum;
import com.rem.chat.websocket.MessageHandler;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * @author: Themberfue
 * @date: 2025/6/21 16:03
 * @description:
 */
@Service
public class ChatServiceImpl implements ChatService {
    private static final Logger log = LoggerFactory.getLogger(ChatServiceImpl.class);
    @Resource
    ChatMessageMapper chatMessageMapper;

    @Resource
    ChatSessionMapper chatSessionMapper;

    @Resource
    UserContactMapper userContactMapper;

    @Resource
    RedisComponent redisComponent;

    @Resource
    MessageHandler messageHandler;

    @Resource
    AppConfig appConfig;

    @Override
    public SendMessageResponse sendMessage(SendMessageRequest param, UserLoginDTO user) {
        // Step 1. 校验消息类型
        MessageTypeEnum messageType= validateMessageType(param.getMessageType());

        // Step 2. 判断是否为好友或加入群聊
        UserContactTypeEnum contactType = validateFriendOrInGroup(param.getContactId(), user.getUserId());

        // Step 3. 获得一些数据...
        String sessionId;
        if (UserContactTypeEnum.USER.equals(contactType)) {
            sessionId = StringUtil.getSessionId(new String[]{user.getUserId(), param.getContactId()});
        } else {
            sessionId = StringUtil.getSessionId(new String[]{param.getContactId()});
        }
        String messageContent = StringUtil.cleanHTMLTag(param.getMessageContent());
        Integer status = messageType.equals(MessageTypeEnum.MEDIA_CHAT) ? MessageStatusEnum.SENDING.getStatus() : MessageStatusEnum.SENT.getStatus();

        // Step 4. 更新会话
        ChatSessionDO chatSessionDO = ChatSessionDO.builder()
                .sessionId(sessionId)
                .lastMessage(messageContent)
                .lastReceiveTime(System.currentTimeMillis())
                .build();
        chatSessionMapper.updateById(chatSessionDO);

        // Step 5. 记录消息
        ChatMessageDO chatMessageDO = ChatMessageDO.builder()
                .sessionId(sessionId)
                .messageContent(messageContent)
                .messageType(param.getMessageType())
                .sendUserId(user.getUserId())
                .sendUserNickName(user.getNickName())
                .contactId(param.getContactId())
                .contactType(contactType.getType())
                .fileSize(param.getFileSize())
                .fileName(param.getFileName())
                .status(status)
                .build();
        chatMessageMapper.insert(chatMessageDO);

        MessageSendDTO<Object> messageSendDTO = BeanUtil.copyProperties(chatMessageDO, MessageSendDTO.class);
        // Step 6. 发送消息
        if (Constants.ROBOT_UID.equals(param.getContactId())) {
            UserLoginDTO robot = new UserLoginDTO();
            robot.setUserId(Constants.ROBOT_UID);
            robot.setNickName(Constants.REM_CHAT);
            SendMessageRequest robotMessage = new SendMessageRequest();
            robotMessage.setContactId(user.getUserId());
            // TODO 对接AI，实现聊天
            robotMessage.setMessageContent("..........");
            robotMessage.setMessageType(MessageTypeEnum.CHAT.getType());
            sendMessage(robotMessage, robot);
        } else {
            messageHandler.sendMessage(messageSendDTO);
        }
        return BeanUtil.copyProperties(messageSendDTO, SendMessageResponse.class);
    }

    @Override
    public Boolean uploadFile(String userId, Integer messageId, MultipartFile file, MultipartFile cover) throws IOException {
        // Step 1. 校验消息正确性
        ChatMessageDO chatMessageDO = validateMessage(userId, messageId);

        // Step 2. 校验文件合法性
        validateFile(file);

        // Step 3. 上传文件到服务器
        uploadFileToServer(messageId, file, cover);

        // Step 4. 更新消息信息
        UpdateWrapper<ChatMessageDO> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda()
                .eq(ChatMessageDO::getMessageId, messageId)
                .eq(ChatMessageDO::getStatus, MessageStatusEnum.SENDING.getStatus()) // ! 乐观锁
                .set(ChatMessageDO::getStatus, MessageStatusEnum.SENT.getStatus());
        chatMessageMapper.update(updateWrapper);

        // Step 5. 发送消息到接受方
        MessageSendDTO<Object> messageSendDTO = MessageSendDTO.builder()
                .contactId(chatMessageDO.getContactId())
                .messageId(messageId)
                .messageType(MessageTypeEnum.FILE_UPLOAD.getType())
                .status(MessageStatusEnum.SENT.getStatus())
                .build();
        messageHandler.sendMessage(messageSendDTO);

        return true;
    }

    @Override
    public File downloadFile(UserLoginDTO user, String fileId, Boolean showCover) {
        File file;
        if (!StringUtil.isNumber(fileId)) {
            String folderPath = appConfig.getProjectFolder() + Constants.FILE_FOLDER_AVATAR;
            String avatarPath = folderPath + fileId + Constants.IMAGE_SUFFIX;
            if (showCover) {
                avatarPath = folderPath + fileId + Constants.COVER_IMAGE_SUFFIX;
            }
            file = new File(avatarPath);
            if (!file.exists()) {
                throw new ServiceException(ServiceErrorCodeConstants.AVATAR_NOT_EXISTS);
            }
        } else {
            ChatMessageDO chatMessageDO = chatMessageMapper.selectById(fileId);
            if (null == chatMessageDO) {
                throw new ServiceException(ServiceErrorCodeConstants.CHAT_IS_NOT_EXISTS);
            }
            String contactId = chatMessageDO.getContactId();
            if (UserContactTypeEnum.USER.equals(UserContactTypeEnum.getByPrefix(contactId)) &&
                    !contactId.equals(user.getUserId())) {
                throw new ServiceException(ServiceErrorCodeConstants.CHAT_IS_NOT_BY_USER_REV);
            } else {
                QueryWrapper<UserContactDO> queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda()
                        .eq(UserContactDO::getUserId, user.getUserId())
                        .eq(UserContactDO::getContactId, contactId)
                        .eq(UserContactDO::getContactType, UserContactTypeEnum.GROUP.getType())
                        .eq(UserContactDO::getStatus, UserContactStatusEnum.FRIEND.getStatus());
                if (0L == userContactMapper.selectCount(queryWrapper)) {
                    throw new ServiceException(ServiceErrorCodeConstants.CHAT_IS_NOT_BY_USER_REV);
                }
            }
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyMM");
            String yMonth = dateFormat.format(new Date());
            String folderPath = appConfig.getProjectFolder() + Constants.FILE_FOLDER_FILE + yMonth;
            File folder = new File(folderPath);
            if (!folder.exists()) {
                folder.mkdirs();
            }
            String fileSuffix = StringUtil.getFileSuffix(chatMessageDO.getFileName());
            String fileName = chatMessageDO.getMessageId() + fileSuffix;
            if (showCover) {
                fileName = fileName + Constants.COVER_IMAGE_SUFFIX;
            }
            file = new File(folder + "/" + fileName);
            if (!file.exists()) {
                log.error("文件不存在");
                throw new ServiceException(ServiceErrorCodeConstants.FILE_IS_NOT_EXISTS);
            }
        }
        return file;
    }

    private void uploadFileToServer(Integer messageId, MultipartFile file, MultipartFile cover) throws IOException {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyMM");
        String yMonth = dateFormat.format(new Date());
        String folderPath = appConfig.getProjectFolder() + Constants.FILE_FOLDER_FILE + yMonth;
        File folder = new File(folderPath);
        if (!folder.exists()) {
            folder.mkdirs();
        }
        String fileSuffix = StringUtil.getFileSuffix(file.getOriginalFilename());
        String fileName = messageId + fileSuffix;
        File uploadFile = new File(folder + "/" + fileName);
        File uploadCover = new File(folder + "/" + fileName + Constants.COVER_IMAGE_SUFFIX);
        file.transferTo(uploadFile);
        if (null != cover) {
            cover.transferTo(uploadCover);
        }
    }

    private void validateFile(MultipartFile file) {
        if (null == file) {
            throw new ServiceException(ServiceErrorCodeConstants.FILE_IS_NOT_EXISTS);
        }

        String fileSuffix = StringUtil.getFileSuffix(file.getOriginalFilename());
        if (!StringUtils.hasText(fileSuffix)) {
            throw new ServiceException(ServiceErrorCodeConstants.FILE_SUFFIX_IS_NULL);
        }
        fileSuffix = fileSuffix.toLowerCase();

        SystemSettingResponse systemSetting = redisComponent.getSystemSetting();
        if (ArrayUtils.contains(Constants.IMAGE_SUFFIX_LIST, fileSuffix) &&
                systemSetting.getMaxImageSize() * Constants.FILE_SIZE_MB < file.getSize()) {
            throw new ServiceException(ServiceErrorCodeConstants.FILE_IS_TOO_MAX);
        }

        if (ArrayUtils.contains(Constants.VIDEO_SUFFIX_LIST, fileSuffix) &&
                systemSetting.getMaxVideoSize() * Constants.FILE_SIZE_MB < file.getSize()) {
            throw new ServiceException(ServiceErrorCodeConstants.FILE_IS_TOO_MAX);
        }

        if (!ArrayUtils.contains(Constants.IMAGE_SUFFIX_LIST, fileSuffix) &&
                !ArrayUtils.contains(Constants.VIDEO_SUFFIX_LIST, fileSuffix) &&
                systemSetting.getMaxFileSize() * Constants.FILE_SIZE_MB < file.getSize()) {
            throw new ServiceException(ServiceErrorCodeConstants.FILE_IS_TOO_MAX);
        }
    }

    private ChatMessageDO validateMessage(String userId, Integer messageId) {
        ChatMessageDO chatMessageDO = chatMessageMapper.selectById(messageId);
        if (null == chatMessageDO) {
            throw new ServiceException(ServiceErrorCodeConstants.CHAT_IS_NOT_EXISTS);
        }

        if (!chatMessageDO.getSendUserId().equals(userId)) {
            throw new ServiceException(ServiceErrorCodeConstants.CHAT_IS_NOT_BY_USER_SEND);
        }

        return chatMessageDO;
    }

    private UserContactTypeEnum validateFriendOrInGroup(String contactId, String userId) {
        UserContactTypeEnum contactType = UserContactTypeEnum.getByPrefix(contactId);
        if (!Constants.ROBOT_UID.equals(contactId)) {
            List<String> contactList = redisComponent.getUserContactList(userId);
            if (!contactList.contains(contactId)) {
                if (null == contactType) {
                    throw new ServiceException(ServiceErrorCodeConstants.CONTACT_TYPE_WRONG);
                }
                if (contactType.equals(UserContactTypeEnum.USER)) {
                    throw new ServiceException(ServiceErrorCodeConstants.USER_IS_NOT_YOUR_FRIEND);
                }
                if (contactType.equals(UserContactTypeEnum.GROUP)) {
                    throw new ServiceException(ServiceErrorCodeConstants.NOT_ENTER_GROUP);
                }
            }
        }
        return contactType;
    }

    private MessageTypeEnum validateMessageType(Integer type) {
        MessageTypeEnum typeEnum = MessageTypeEnum.getByStatus(type);
        if (null == typeEnum ||
                ArrayUtils.contains(new Integer[]{MessageTypeEnum.CHAT.getType(), MessageTypeEnum.MEDIA_CHAT.getType()},
                        typeEnum.getType())) {
            throw new ServiceException(ServiceErrorCodeConstants.MESSAGE_TYPE_WRONG);
        }
        return typeEnum;
    }
}
